/******************************************************
*  Copyright(c) pages.ilovezxl.top
*  FileName : fs_fat32.c
*  Author : rivulet.dave
*  Description : FAT32文件系统接口
*  Data : 2020-09-10
******************************************************/

/*********************************************************************
 * INCLUDES
 */

#include "stdrope.h"
#include "sallib.h"
#include "salagent.h"


#if (SAL_FS == _ENABLE && SAL_FS_PRODUCER == _FS_FAT32_)

#include "log.h"
#include "fs.h"
#include "fs_fat32.h"

/*********************************************************************
 *  EXTERNAL VARIABLES
 */

/*********************************************************************
 * MACROS
 */

static bool s_bIsLogEnable = true;
#define FS_LOG(level, ...) do{if (s_bIsLogEnable) LOG_Output("MOD_FS", level, 0,  __FILENAME__, __LINE__, ##__VA_ARGS__);}while(0)

/*********************************************************************
 * TYPES
 */

/*********************************************************************
 * FUNCTIONS DECLARE
 */

static FRESULT_E fsOpenByFat32(fd_t *phFile, const char* pcPath, uint8_t ucMode);
static FRESULT_E fsCloseByFat32(fd_t *phFile);
static int32_t   fsReadByFat32(fd_t *phFile, void *pBuff, uint32_t uiSize);
static int32_t   fsReadLineByFat32(fd_t *phFile, void *pBuff, uint32_t uiSize);
static int32_t   fsWriteByFat32(fd_t *phFile, const void *pBuff, uint32_t uiSize);
static FRESULT_E fsSeekByFat32(fd_t *phFile, int32_t iOffset, uint8_t ucWhence);
static FRESULT_E fsDeleteByFat32(const char *pName);
static FRESULT_E fsGetSizeByFat32(fd_t *phFile, uint32_t *puiSize);
static FRESULT_E fsFlushByFat32(fd_t *phFile);
static FRESULT_E fsRenameByFat32(const char *pOldPath, const char *pNewPath);
static FRESULT_E fsMkDirByFat32(const char *pPath);
static FRESULT_E fsDirOpenByFat32(const char *pcDirPath, FSDIR_S *pstDir);
static FRESULT_E fsDirCloseByFat32(FSDIR_S *pstDir);
static FRESULT_E fsDirReadByFat32(FSDIR_S *pstDir, FILE_INFO_S *pstFileInfo);
static char *    fsGetCwdByFat32(char *pBuf, uint32_t uiSize);
static FRESULT_E fsChDirByFat32(const char *pPath);
static FRESULT_E fsIsExistByFat32(const char *pPath);
static FRESULT_E fsChModByFat32(const char *pPath, uint32_t uiMode);

/*********************************************************************
 *  VARIABLES
 */

static FS_BASE_S s_stFSBaseFat32Object;

/*********************************************************************
 * FUNCTIONS
 */

/**
 *   @Description: Fat32文件系统初始化
 *   @param : NA
 *   @return: 文件系统对象
 */
FS_BASE_S *fsFat32ObjNew(void)
{
    s_stFSBaseFat32Object.pfFSOpen = fsOpenByFat32;
    s_stFSBaseFat32Object.pfFSClose = fsCloseByFat32;
    s_stFSBaseFat32Object.pfFSRead = fsReadByFat32;
    s_stFSBaseFat32Object.pfFSReadLine = fsReadLineByFat32;
    s_stFSBaseFat32Object.pfFSWrite = fsWriteByFat32;
    s_stFSBaseFat32Object.pfFSSeek = fsSeekByFat32;
    s_stFSBaseFat32Object.pfFSDelete = fsDeleteByFat32;
    s_stFSBaseFat32Object.pfFSGetSize = fsGetSizeByFat32;
    s_stFSBaseFat32Object.pfFSFlush = fsFlushByFat32;
    s_stFSBaseFat32Object.pfFSRename = fsRenameByFat32;
    s_stFSBaseFat32Object.pfFSMkDir = fsMkDirByFat32;
    s_stFSBaseFat32Object.pfFSDirOpen = fsDirOpenByFat32;
    s_stFSBaseFat32Object.pfFSDirClose = fsDirCloseByFat32;
    s_stFSBaseFat32Object.pfFSDirRead = fsDirReadByFat32;
    s_stFSBaseFat32Object.pfFSGetCwd = fsGetCwdByFat32;
    s_stFSBaseFat32Object.pfFSChDir = fsChDirByFat32;
    s_stFSBaseFat32Object.pfFSIsExist = fsIsExistByFat32;
    s_stFSBaseFat32Object.pfFSChMod = fsChModByFat32;

    MX_FATFS_Init();

    return &s_stFSBaseFat32Object;
}

/**
 *   @Description: Fat32文件系统对象删除
 *   @param : NA
 *   @return: NA
 */
void fsFat32ObjDelete(void)
{
}

static FRESULT_E fsOpenByFat32(fd_t *phFile, const char* pcPath, uint8_t ucMode)
{
    BYTE accessMode = 0;
    FRESULT result = FR_OK;

    if (!phFile)
    {
        return FS_INVALID_OBJECT;
    }

    if (_GET_BIT(ucMode, F_WRONLY))
    {
        _SET_BIT(accessMode, FA_WRITE);
    }
    else
    {
        _SET_BIT(accessMode, FA_READ);
    }

    if (_GET_BIT(ucMode, F_CREAT))
    {
        _SET_BIT(accessMode, FA_OPEN_ALWAYS);
    }

    result = f_open(phFile, pcPath, accessMode);
    return result;
}

static FRESULT_E fsCloseByFat32(fd_t *phFile)
{
    if (!phFile)
    {
        return FS_INVALID_PARAMETER;
    }

    if (0 != f_close(phFile))
    {
        FS_LOG(LOG_LEVEL_ERROR, "file close fail.\r\n");
    }
    return FS_OK;
}

static int32_t   fsReadByFat32(fd_t *phFile, void *pBuff, uint32_t uiSize)
{
    uint32_t uiReadBytes = 0;
    FRESULT iResult = 0;

    if (!phFile)
    {
        return FS_INVALID_PARAMETER;
    }

    if (NULL == pBuff)
    {
        return FS_INVALID_PARAMETER;
    }
    
    iResult = f_read(phFile, pBuff, uiSize, (UINT*)&uiReadBytes);
    if (!iResult)
    {
        FS_LOG(LOG_LEVEL_ERROR, "file read fail:%d.\r\n", iResult);
        return FS_READ_ERR;
    }
    
    return (int32_t)uiReadBytes;
}

static int32_t   fsReadLineByFat32(fd_t *phFile, void *pBuff, uint32_t uiSize)
{
    uint32_t uiReadCount = 0;
    char cReadBuffer;
    int32_t iTotalCount = 0;
    char *pcBuffer = (char *)pBuff;

    if (!phFile)
    {
        return FS_INVALID_PARAMETER;
    }

    while (0 == f_read(phFile, &cReadBuffer, 1, (UINT*)&uiReadCount))
    {
        pcBuffer[iTotalCount++] = cReadBuffer;
        if (iTotalCount == uiSize - 1)
        {
            break;
        }
        if (cReadBuffer == '\n' || cReadBuffer == EOF)
        {
            break;
        }

    }

    pcBuffer[iTotalCount] = '\0';
    return iTotalCount;
}

static int32_t   fsWriteByFat32(fd_t *phFile, const void *pBuff, uint32_t uiSize)
{
    FRESULT iResult = 0;
    uint32_t uiWriteBytes = 0;

    if (!phFile)
    {
        return FS_INVALID_PARAMETER;
    }

    if (NULL == pBuff)
    {
        return FS_INVALID_PARAMETER;
    }
    
    iResult = f_write(phFile, pBuff, uiSize, (UINT*)&uiWriteBytes);
    if (0 != iResult)
    {
        FS_LOG(LOG_LEVEL_ERROR, "file write fail:%d.\r\n", iResult);
        return FS_WRITE_ERR;
    }
    
    return (int32_t)uiWriteBytes;
}

static FRESULT_E fsSeekByFat32(fd_t *phFile, int32_t iOffset, uint8_t ucWhence)
{
    FRESULT iRet;

    if (!phFile)
    {
        return FS_INVALID_PARAMETER;
    }

    if (SEEK_HEAD == ucWhence)
    {
        f_lseek(phFile, 0);
    }

    if (SEEK_TAIL == ucWhence)
    {
        f_lseek(phFile, f_size(phFile));
    }
    
    iRet = f_lseek(phFile, iOffset);
    if (0 != iRet)
    {
        FS_LOG(LOG_LEVEL_ERROR, "file seek fail:%d.\r\n", iRet);
        return FS_OTHER_ERR;
    }
    
    return FS_OK;
}

static FRESULT_E fsDeleteByFat32(const char *pName)
{
    if (0 > f_unlink(pName))
    {
        FS_LOG(LOG_LEVEL_ERROR, "file delete fail.\r\n");
        return FS_OTHER_ERR;
    }
    return FS_OK;
}

static FRESULT_E fsGetSizeByFat32(fd_t *phFile, uint32_t *puiSize)
{
    if (!phFile)
    {
        return FS_INVALID_PARAMETER;
    }
    *puiSize = f_size(phFile);
    return FS_OK;
}

static FRESULT_E fsFlushByFat32(fd_t *phFile)
{
    if (!phFile)
    {
        return FS_INVALID_PARAMETER;
    }
    f_sync(phFile);
    return FS_OK;
}

static FRESULT_E fsRenameByFat32(const char *pOldPath, const char *pNewPath)
{
    f_rename(pOldPath, pNewPath);
    return FS_OK;
}

static FRESULT_E fsMkDirByFat32(const char *pPath)
{
    f_mkdir(pPath);
    return FS_OK;
}

static FRESULT_E fsDirOpenByFat32(const char *pcDirPath, FSDIR_S *pstDir)
{
    f_opendir((DIR *)pstDir, pcDirPath);
    return FS_OK;
}

static FRESULT_E fsDirCloseByFat32(FSDIR_S *pstDir)
{
    f_closedir((DIR *)pstDir);
    return FS_OK;
}

static FRESULT_E fsDirReadByFat32(FSDIR_S *pstDir, FILE_INFO_S *pstFileInfo)
{
    FILINFO stFileInfo;
    
    if (FR_OK == f_readdir((DIR *)pstDir, &stFileInfo))
    {
        strcpy(pstFileInfo->acFileName, stFileInfo.fname);
        return FS_OK;
    }

    return FS_NO_FILE;
}

static char *    fsGetCwdByFat32(char *pBuf, uint32_t uiSize)
{
    f_getcwd(pBuf, uiSize);
    return pBuf;
}

static FRESULT_E fsChDirByFat32(const char *pPath)
{
    if (0 > f_chdir(pPath))
    {
        return FS_OTHER_ERR;
    }
    return FS_OK;
}

static FRESULT_E fsIsExistByFat32(const char *pPath)
{
    FILINFO stFileStat;
    if (FR_OK == f_stat(pPath, &stFileStat))
    {
        return FS_OK;
    }

    return FS_NO_FILE;
}

static FRESULT_E fsChModByFat32(const char *pPath, uint32_t uiMode)
{
    return FS_OK;
}

#endif  // SAL_FS == _ENABLE

/************************************** The End Of File **************************************/

